using System;
using System.Collections;

using Org.BouncyCastle.Asn1;

namespace Org.BouncyCastle.Asn1.Pkcs
{
	/**
	 * a Pkcs#7 signed data object.
	 */
	public class SignedData
		: Asn1Encodable
	{
		private readonly DerInteger		version;
		private readonly Asn1Set		digestAlgorithms;
		private readonly ContentInfo	contentInfo;
		private readonly Asn1Set		certificates;
		private readonly Asn1Set		crls;
		private readonly Asn1Set		signerInfos;

		public static SignedData GetInstance(
			object obj)
		{
			if (obj is SignedData)
			{
				return (SignedData) obj;
			}

			if (obj is Asn1Sequence)
			{
				return new SignedData((Asn1Sequence) obj);
			}

			throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "obj");
		}

		public SignedData(
			DerInteger        _version,
			Asn1Set           _digestAlgorithms,
			ContentInfo       _contentInfo,
			Asn1Set           _certificates,
			Asn1Set           _crls,
			Asn1Set           _signerInfos)
		{
			version          = _version;
			digestAlgorithms = _digestAlgorithms;
			contentInfo      = _contentInfo;
			certificates     = _certificates;
			crls             = _crls;
			signerInfos      = _signerInfos;
		}

		private SignedData(
			Asn1Sequence seq)
		{
			IEnumerator e = seq.GetEnumerator();

			e.MoveNext();
			version = (DerInteger) e.Current;

			e.MoveNext();
			digestAlgorithms = (Asn1Set) e.Current;

			e.MoveNext();
			contentInfo = ContentInfo.GetInstance(e.Current);

			while (e.MoveNext())
			{
				Asn1Object o = (Asn1Object) e.Current;

				//
				// an interesting feature of SignedData is that there appear to be varying implementations...
				// for the moment we ignore anything which doesn't fit.
				//
				if (o is DerTaggedObject)
				{
					DerTaggedObject tagged = (DerTaggedObject) o;

					switch (tagged.TagNo)
					{
						case 0:
							certificates = Asn1Set.GetInstance(tagged, false);
							break;
						case 1:
							crls = Asn1Set.GetInstance(tagged, false);
							break;
						default:
							throw new ArgumentException("unknown tag value " + tagged.TagNo);
					}
				}
				else
				{
					signerInfos = (Asn1Set) o;
				}
			}
		}

		public DerInteger Version
		{
			get { return version; }
		}

		public Asn1Set DigestAlgorithms
		{
			get { return digestAlgorithms; }
		}

		public ContentInfo ContentInfo
		{
			get { return contentInfo; }
		}

		public Asn1Set Certificates
		{
			get { return certificates; }
		}

		public Asn1Set Crls
		{
			get { return crls; }
		}

		public Asn1Set SignerInfos
		{
			get { return signerInfos; }
		}

		/**
		 * Produce an object suitable for an Asn1OutputStream.
		 * <pre>
		 *  SignedData ::= Sequence {
		 *      version Version,
		 *      digestAlgorithms DigestAlgorithmIdentifiers,
		 *      contentInfo ContentInfo,
		 *      certificates
		 *          [0] IMPLICIT ExtendedCertificatesAndCertificates
		 *                   OPTIONAL,
		 *      crls
		 *          [1] IMPLICIT CertificateRevocationLists OPTIONAL,
		 *      signerInfos SignerInfos }
		 * </pre>
		 */
		public override Asn1Object ToAsn1Object()
		{
			Asn1EncodableVector v = new Asn1EncodableVector(
				version, digestAlgorithms, contentInfo);

			if (certificates != null)
			{
				v.Add(new DerTaggedObject(false, 0, certificates));
			}

			if (crls != null)
			{
				v.Add(new DerTaggedObject(false, 1, crls));
			}

			v.Add(signerInfos);

			return new BerSequence(v);
		}
	}
}
